package com.demo.base.tokenLogin.interceptor;

import com.demo.base.exception.AiException;
import com.demo.base.tokenLogin.annotation.DisableToken;
import com.demo.base.tokenLogin.model.UserDto;
import com.demo.base.tokenLogin.token.JwtHelper;
import com.demo.base.util.SnowFlakeUtil;
import com.demo.constants.AppConstants;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import static com.demo.constants.AppConstants.APP_REQUEST_ATTRIBUTE;
import static com.demo.constants.AppConstants.AUTHORIZATION_HEADER;

/**
 * @author 曳戈泰尔
 * @version 1.0
 * @description 拦截器 注入当前登录用户信息数据到request
 * @date 2023/3/13 3:35 PM
 */
@Component
@Slf4j
public class CurrentUserInterceptor implements HandlerInterceptor {

    /**
     * 该方法将在请求处理之前进行调用，只有该方法返回true，才会继续执行后续的Interceptor和Controller。
     * 当返回值为true时就会继续调用下一个Interceptor的preHandle 方法。 如果已经是最后一个Interceptor，则调用当前请求的Controller方法。
     */
    @Override
    public boolean preHandle(
            HttpServletRequest request, HttpServletResponse response, Object handler) {

        // 记录请求开始时间
        request.setAttribute("start_time", System.currentTimeMillis());

        // 设置请求ID
        if (StringUtils.isBlank(MDC.get(AppConstants.HEADER_RID_NAME))) {
            MDC.put(AppConstants.HEADER_RID_NAME, SnowFlakeUtil.getId().toString());
        }

        // 如果不是映射到方法直接通过
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }

        /** DisableToken注解检验 * */
        HandlerMethod method = (HandlerMethod) handler;
        DisableToken auth = method.getMethod().getAnnotation(DisableToken.class);
        if (auth != null) {
            return true;
        }

        // 获取用户token信息：token可能在请求头、请求参数、cookie中
        // 从请求头获取token
        String token = request.getHeader(AUTHORIZATION_HEADER);
        if (StringUtils.isEmpty(token)) {
            // 从请求参数获取token
            // 支持请求Headers：multipart/form-data
            token = request.getParameter(AUTHORIZATION_HEADER);
            if (StringUtils.isEmpty(token)) {
                // 从cookie获取token
                Cookie[] cookies = request.getCookies();
                if (cookies != null) {
                    for (Cookie cookie : cookies) {
                        if (cookie.getName().compareTo(AUTHORIZATION_HEADER) == 0) {
                            token = cookie.getValue();
                            break;
                        }
                    }
                }
            }
        }
        // 如果未获取到token
        if (StringUtils.isEmpty(token)) {
            throw new AiException("请求失败，请检查登录状态");
        }

        // 获取token对应的用户信息
        UserDto userDto = JwtHelper.parseToken(token);

        // 传递user信息
        request.setAttribute(APP_REQUEST_ATTRIBUTE, userDto);

        // 拦截器向下继续传递
        return true;
    }

    /**
     * 在当前控制器方法执行之后执行
     *
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     */
    @Override
    public void postHandle(
            HttpServletRequest request,
            HttpServletResponse response,
            Object handler,
            ModelAndView modelAndView) {

        // 获取请求路径
        String requestPath = request.getServletPath();

        // 计算请求花费时间
        long startTime = (long) request.getAttribute("start_time");
        long endTime = System.currentTimeMillis();
        long costTime = endTime - startTime;
        log.info("requestPath={}，总耗时={}ms", requestPath, costTime);

        // 清除请求ID，请求完成之后就不需要这个ID了，需要删除掉
        MDC.remove(AppConstants.HEADER_RID_NAME);
    }

    /**
     * 该方法会在整个请求完成，即视图渲染结束之后执行。
     *
     * @param request
     * @param response
     * @param object
     * @param exception
     */
    @Override
    public void afterCompletion(
            HttpServletRequest request,
            HttpServletResponse response,
            Object object,
            Exception exception) {

        if (StringUtils.isNotBlank(MDC.get(AppConstants.HEADER_RID_NAME))) {
            // 清除请求ID，请求完成之后就不需要这个ID了，需要删除掉
            MDC.remove(AppConstants.HEADER_RID_NAME);
        }
    }
}
